<html>
<head>
<style>
#test-hover {
    position: absolute;
    left: 20px;
    top: 40px;
    width: 100px;
    height: 30px;
    background-color: red;
}

#test-hover:hover {
    background-color: blue;
}

#test-button {
    position: absolute;
    left: 20px;
    top: 100px;
    width: 100px;
    height: 30px;
}

#scrollable {
    position: absolute;
    left: 200px;
    top: 30px;
    width: 200px;
    height: 200px;
    overflow: scroll;
}

#scrolled {
    width: 800px;
    height: 800px;
}
</style>
</head>
<body>
<div id="test-hover">Hover me</div>
<button id="test-button">Click me</button>
<div id="scrollable"><div id="scrolled"></div></div>
<script>
var pendingListeners = {};
var pendingEvents = {};
var inflightEvents = null;
function onEvent(event)
{
    if (inflightEvents) {
        inflightEvents.push(event);
        return;
    }
    inflightEvents = [event];
    if (event.type === "mousemove")
        document.body.style.backgroundColor = "#" + Math.floor(Math.random() * (1 << 24)).toString(16);
    requestAnimationFrame(onEventAfterFrame);
}

function onEventAfterFrame()
{
    for (event of inflightEvents) {
        var type = event.type;
        var listener = pendingListeners[type];
        if (!listener) {
            pendingEvents[type] = (pendingEvents[type] || 0) + 1;
            continue;
        }
        delete pendingListeners[type];
        listener();
    }
    inflightEvents = null;
}

function waitForEvent(eventType, callback)
{
    if (pendingEvents[eventType]) {
        pendingEvents[eventType]--;
        callback();
        return;
    }
    pendingListeners[eventType] = callback;
}

var eventTypes = [
    "mousemove",
    "mousedown",
    "mouseup",
    "wheel",
    "gesturetap",
    "click",
    "keydown",
    "keyup",
    "touchstart",
    "touchend",
    "touchcancel",
    "touchmove"
];

for (var e of eventTypes) {
    window.addEventListener(e, onEvent, true);
}
</script>
</body>
</html>
